home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 22 / Cream of the Crop 22.iso / bbs / pprd199.zip / PPRD.C < prev    next >
C/C++ Source or Header  |  1996-10-06  |  25KB  |  1,027 lines

  1. /*
  2.  
  3. PPRD
  4.  
  5. Line printer daemon using Berkeley LPD protocol on top of WATTCP for up
  6. to 3 parallel printer ports.
  7.  
  8. An alternate protocol called JD is supported also. In this the daemon
  9. waits on port 9100 and copies data to the printer until EOF.
  10.  
  11. This daemon is written as a state machine because it has to support
  12. several connections on a single threaded system, DOS.
  13.  
  14. When started up, it checks to see how many printers are known to the
  15. BIOS. These printers are served as LPT[123].  It listens on the
  16. standard LPD port 515, but this can be changed from the command line.
  17.  
  18. Examples of options:
  19.  
  20. -p1515    listen on port 1515 instead
  21. -j9200    use direct protocol instead at port 9200 (defaults to 9100 if unspec)
  22. -23    disable printers 2 and 3
  23. -n2    two printers, no matter what BIOS claims
  24. -b12    on 1 and 2 bypass BIOS and send directly to port (parallel ports only)
  25. -t    don't indicate available printers with tones
  26. -i    reinitialise printer via hardware line on job abort
  27. -k    abort job if client drops connection
  28. -s    disable subnet match (server and client must be on same subnet)
  29. -alist    comma separated list of up to 20 hosts/networks allowed connection
  30. -dlist    comma separated list of up to 20 hosts/networks denied connection
  31.     last two are mutually exclusive
  32. -lhost    log diagnostics to host of that domain name
  33. -c    output to console (for debugging only)
  34. -g    display memory buffer status graphically
  35. -m    measure transfer rate, don't print
  36.  
  37. Jobs can be aborted by C-F1 through C-F3 for that printer.
  38.  
  39. The LPD protocol is summarised below where S = server and C = client.
  40. Ack = '\0' and nak = '\001'.
  41.  
  42. C: \002printer\n
  43. S: ack/nak
  44. C: \003size datafilename\n    (size is bytes as decimal string)
  45. S: ack/nak
  46. C: size bytes of data + one '\0' byte
  47. S: ack/nak
  48. C: \002size cntlfilename\n
  49. S: ack/nak
  50. C: size bytes of control + one '\0' byte
  51. S: ack/nak
  52.  
  53. The data and control files can occur in either order.  In this
  54. implementation the filenames are ignored.
  55.  
  56. If the control file is sent first, then a zero length data file indicates
  57. that EOF == EOJ. This is a RFC1179 extension to existing lpds.
  58.  
  59. This program was compiled under Borland C++ 3.1 in C mode for the compact
  60. model. You will require the WATTCP libraries. A copy is included in the
  61. distribution. For the sources of WATTCP, ask archie where the archives
  62. are.
  63.  
  64. Please send bug fixes, ports and enhancements to the author for
  65. incorporation in newer versions.
  66.  
  67. Copyright (C) 1996  Ken Yap (ken@syd.dit.csiro.au)
  68.  
  69. This program is free software; you can redistribute it and/or modify it
  70. under the terms of the Artistic License, a copy of which is included
  71. with this distribution.
  72.  
  73. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
  74. IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  75. WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  76.  
  77. */
  78.  
  79. #include    <stdio.h>
  80. #include    <stdlib.h>
  81. #include    <string.h>
  82. #include    <memory.h>
  83. #include    <sys/types.h>
  84. #include    <time.h>
  85.  
  86. #include    <tcp.h>
  87.  
  88. #include    <alloc.h>
  89. #include    <dos.h>
  90. #include    <bios.h>
  91. #include    <conio.h>
  92.  
  93. #include    "pprd.h"
  94.  
  95. #define        PROGRAM        "PPRD"
  96. #define        VERSION        "Version 1.99 15 Oct 1996"
  97. #define        AUTHOR        "Copyright (C) 1996 Ken Yap (ken@syd.dit.csiro.au)"
  98.  
  99. #define        MAXLPT        3    /* supported under DOS */
  100. #define        MAXCON        (MAXLPT+1)    /* +1 for lpq, and direct */
  101. /* try this many times to see if can accept next byte right away */
  102. #define        POLL_MAX    10
  103.  
  104. #define        MDEBUG            /* enable code to debug memory usage */
  105.  
  106. unsigned int        lpdport = LPDPORT;
  107. unsigned int        jdport = JDPORT;
  108. int            jdprotocol = 0;    /* use direct protocol if != 0 */
  109. int            nlpt = 0;
  110. struct lpt_info        lpt[MAXLPT];
  111. int            notone = 0;
  112. int            reinit = 0;    /* reinitialise on abort */
  113. int            killjob = 1;    /* abort on connection job */
  114. unsigned int        lpt_tone[MAXLPT] = { 523, 587, 659 };    /* CDE */
  115. char            lpt_names[MAXLPT][16] = { "lpt1", "lpt2", "lpt3" };
  116. struct conn_info    conn[MAXCON];    /* one extra for lpq/lprm requests */
  117. struct conn_info    log;        /* for connection to syslogd */
  118. unsigned int        buffer_size;    /* number of bytes per conn buffer */
  119. int            toconsole = 0;    /* write to console for debugging */
  120. int            graphmem = 0;    /* display memory usage graph */
  121. int            measureonly = 0;/* measure transfer rate only */
  122. char            *loghost = 0;
  123. int            check_subnet = 1;
  124. int            nallow = 0, ndeny = 0;
  125. longword        allow[20], deny[20];
  126. void                (*normal_init)(char *name, char *value);
  127.  
  128. #define        MAXALLOW    (sizeof(allow)/sizeof(allow[0]))
  129. #define        MAXDENY        (sizeof(deny)/sizeof(deny[0]))
  130. #define        is_net_addr(a)    (((a) & ~sin_mask) == 0)
  131. #define        is_on_net(a,n)    (((a) & sin_mask) == (n))
  132.  
  133. extern longword        my_ip_addr;
  134. extern longword        sin_mask;
  135.  
  136. #define    ack_cmd(c)    (void)sock_fastwrite(&c->sock, (byte *)"", 1)
  137. #define    nak_cmd(c)    (void)sock_fastwrite(&c->sock, (byte *)"\001", 1)
  138.  
  139. char *ptime(void)
  140. {
  141.     time_t    t;
  142.     char    *p;
  143.  
  144.     t = time(0);
  145.     p = ctime(&t);
  146.     p[24] = '\0';
  147.     return (p);
  148. }
  149.  
  150. void init_queues(void)
  151. {
  152.     struct conn_info    *c;
  153.     int            i, ret;
  154.     struct lpt_info        *p;
  155.  
  156.     if (nlpt == 0)                /* not overridden by -n? */
  157.     {
  158.         ret = biosequip();
  159.         nlpt = (ret >> 14) & 0x3;
  160.     }
  161.     for (i = 0; i < nlpt; ++i)
  162.     {
  163.         p = &lpt[i];
  164.         if (p->avail == DISABLED)    /* unavail from cmd line? */
  165.             continue;
  166.         (void)bios_printer_init(i);    /* initialise printer */
  167.         sleep(2);            /* let printer settle */
  168.         p->status = bios_printer_status(i);    /* get status */
  169.         if (p->status & (P_TIMEOUT|P_IOERROR))
  170.         {
  171.             p->avail = NONESUCH;    /* printer not available */
  172.             (void)printf("%s: Error initialising %s\n",
  173.                 ptime(), lpt_names[i]);
  174.         }
  175.         else
  176.         {
  177.             p->avail = FREE;    /* printer available */
  178.             (void)printf("%s: %s initialised",
  179.                 ptime(), lpt_names[i]);
  180.             if (p->hwaddr && (ret = peek(0, 0x408 + i * 2)) != 0)
  181.                 (void)printf(", direct hardware access at %#3X",
  182.                     p->hwaddr = ret);
  183.             (void)printf("\n");
  184.         }
  185.     }
  186.     for (i = 0; i < MAXLPT; ++i)
  187.     {
  188.         if (notone || lpt[i].avail != FREE)
  189.             continue;
  190.         sound(lpt_tone[i]);
  191.         sleep(1);
  192.         nosound();
  193.     }
  194.     buffer_size = farcoreleft() / 1024L;    /* work in units of kb */
  195.     printf("%s: %uk free memory, ", ptime(), buffer_size);
  196.     buffer_size -= 2U;            /* for log buffer */
  197.     buffer_size /= MAXCON;            /* divide it up fairly */
  198.     if (buffer_size > BUFFER_SIZE)
  199.         buffer_size = BUFFER_SIZE;
  200.     printf("%uk per buffer\n", buffer_size);
  201.     buffer_size *= 1024U;            /* now work in bytes */
  202.     log.buffer = farmalloc(2048UL);        /* log buffer is fixed length */
  203.     /* should error check above but farmalloc below will bomb out anyway if
  204.        we are out of memory */
  205.     for (i = 0; i < MAXCON; ++i)
  206.     {
  207.         c = &conn[i];
  208.         c->state = INIT;        /* initial connection state */
  209.         c->controlfirst = 0;
  210.         c->printer = -1;
  211.         if ((c->buffer = farmalloc((unsigned long)buffer_size)) == 0)
  212.         {
  213.             printf("Out of heap memory\n");
  214.             exit(1);
  215.         }
  216.     }
  217. }
  218.  
  219. void init_log(void)
  220. {
  221.     longword        logip;
  222.  
  223.     if (loghost == 0 ||
  224.         (logip = resolve(loghost)) == (longword)0 ||
  225.         udp_open(&log.sock, 0, logip, LOGPORT, NULL) == 0)
  226.     {
  227.         loghost = 0;
  228.         return;
  229.     }
  230.     (void)sprintf(log.buffer, LOG_TAG PROGRAM " " VERSION
  231.         ", %d printer(s)\n", nlpt);
  232.     (void)sock_fastwrite(&log.sock, log.buffer, strlen(log.buffer));
  233. }
  234.  
  235. void report_change(char *msg, int printer)
  236. {
  237.     (void)printf("%s: %s %s\n", ptime(), lpt_names[printer], msg);
  238.     if (loghost != 0)        /* log to syslogd */
  239.     {
  240.         (void)sprintf(log.buffer, LOG_TAG "%s %s\n", lpt_names[printer], msg);
  241.         (void)sock_fastwrite(&log.sock, log.buffer, strlen(log.buffer));
  242.     }
  243. }
  244.  
  245. void lpt_status_change(int i, int delta, int status)
  246. {
  247.     if (status & P_TIMEOUT)
  248.         report_change("time out", i);
  249.     if (status & P_IOERROR)
  250.         report_change("I/O error", i);
  251.     if (delta & P_SELECTED)
  252.     {
  253.         if (status & P_SELECTED)
  254.             report_change("online", i);
  255.         else
  256.             report_change("offline", i);
  257.     }
  258.     if (status & P_NOPAPER)
  259.         report_change("paper out", i);
  260. }
  261.  
  262. int same_subnet(longword client)
  263. {
  264.     /* assumes that bit operations can be done on longword */
  265.     return (((client ^ my_ip_addr) & sin_mask) == 0);
  266. }
  267.  
  268. int in_list(longword ip, longword table[], int entries, int maxtable, int answer)
  269. {
  270.     int        i;
  271.  
  272.     for (i = 0; i < entries && i < maxtable; ++i)
  273.         if ((is_net_addr(table[i]) && is_on_net(ip, table[i]))
  274.             || ip == table[i])
  275.             return (answer);
  276.     return (!answer);
  277. }
  278.  
  279. int check_access(longword ip)
  280. {
  281.     if (check_subnet && same_subnet(ip))
  282.         return (1);
  283.     if (nallow > 0)
  284.         return (in_list(ip, allow, nallow, MAXALLOW, 1));
  285.     if (ndeny > 0)
  286.         return (in_list(ip, deny, ndeny, MAXDENY, 0));
  287.     /* otherwise denied */
  288.     return (0);
  289. }
  290.  
  291. /*
  292.  *    Translate from name to printer number
  293.  */
  294. int printernumber(char *printername)
  295. {
  296.     int        i;
  297.  
  298.     for (i = 0; i < MAXLPT; ++i)
  299.         if (stricmp(lpt_names[i], printername) == 0)
  300.             return (i);
  301.     return (-1);
  302. }
  303.  
  304. void queuename(struct conn_info *c)
  305. {
  306.     int        i, current, delta;
  307.     char        printername[sizeof(lpt_names[0])];
  308.  
  309.     *c->bufip = '\0';
  310.     if (*c->buffer != '\002' && *c->buffer != '\003')
  311.     {
  312.         c->state = NAKANDCLOSE;
  313.         return;
  314.     }
  315.     if (sscanf(c->buffer+1, "%15s", printername) != 1 ||
  316.         (i = printernumber(printername)) < 0)
  317.     {
  318.         (void)printf("%s: Printer name error: %s", ptime(), c->buffer+1);
  319.         c->state = NAKANDCLOSE;
  320.         return;
  321.     }
  322.     switch (*c->buffer)
  323.     {
  324.     case '\002':
  325.         if (lpt[i].avail != FREE)
  326.         {
  327.             (void)printf("%s: Printer not available: %s",
  328.                 ptime(), c->buffer+1);
  329.             c->state = NAKANDCLOSE;
  330.             return;
  331.         }
  332.         c->printer = i;
  333.         lpt[i].avail = BUSY;
  334.         (void)printf("%s: Job for %s started\n",
  335.             ptime(), lpt_names[i]);
  336.         c->state = RECVJOB;
  337.         ack_cmd(c);
  338.         break;
  339.     case '\003':
  340.     case '\004':
  341.         (void)sprintf(c->buffer, "%s is ", lpt_names[i]);
  342.         switch (lpt[i].avail)
  343.         {
  344.         case BUSY:
  345.             (void)strcat(c->buffer, "busy\n");
  346.             break;
  347.         case FREE:
  348.             (void)strcat(c->buffer, "available");
  349.             /* get status of printer and save it */
  350.             lpt[i].status = current = bios_printer_status(i);
  351.             (void)strcat(c->buffer, current & P_SELECTED ?
  352.                 " online" : " offline");
  353.             if (current & P_NOPAPER)
  354.                 (void)strcat(c->buffer, " no paper");
  355.             (void)strcat(c->buffer, "\n");
  356.             break;
  357.         default:
  358.             (void)strcat(c->buffer, "not available\n");
  359.             break;
  360.         }
  361.         (void)sock_fastwrite(&c->sock, c->buffer, strlen(c->buffer));
  362.         c->state = CLOSING;
  363.         break;
  364.     }
  365. }
  366.  
  367. void get_jobinfo(struct conn_info *c)
  368. {
  369.     char            *p;
  370.  
  371.     p = c->bufip;
  372.     if (buffer_room(c) == 0)
  373.         --p;
  374.     *p = '\0';
  375.     for (p = strtok(c->buffer, "\n"); p != 0; p = strtok(0, "\n"))
  376.     {
  377.         switch (*p)
  378.         {
  379.         case 'N':
  380.             (void)strncat(c->jobname, p+1, sizeof(c->jobname)-1);
  381.             break;
  382.         case 'P':
  383.             (void)strncat(c->username, p+1, sizeof(c->username)-1);
  384.             break;
  385.         case 'H':
  386.             (void)strncat(c->hostname, p+1, sizeof(c->hostname)-1);
  387.             break;
  388.         }
  389.     }
  390. }
  391.  
  392. #ifdef    MDEBUG
  393. /*
  394.  *    Print graphical trace of buffer utilisation.
  395.  *    63 characters: X or x is used block, - is free.
  396.  *    Small bug: a full buffer displays like an empty one.
  397.  *    Not worth fixing because this is for debugging anyway.
  398.  */
  399. void trace_queue(struct conn_info *c)
  400. {
  401.     int        i;
  402.     unsigned int    ioff, ooff;
  403.  
  404.     ioff = c->bufip - c->buffer;
  405.     ooff = c->bufop - c->buffer;
  406.     ioff /= (buffer_size / 64);
  407.     ooff /= (buffer_size / 64);
  408.     if (ioff < ooff)
  409.     {
  410.         for (i = 0; i < ioff; ++i)
  411.             putchar('X');
  412.         for ( ; i < ooff; ++i)
  413.             putchar('-');
  414.         for ( ; i < 63; ++i)
  415.             putchar('x');
  416.     }
  417.     else
  418.     {
  419.         for (i = 0; i < ooff; ++i)
  420.             putchar('-');
  421.         for ( ; i < ioff; ++i)
  422.             putchar('X');
  423.         for ( ; i < 63; ++i)
  424.             putchar('-');
  425.     }
  426.     printf(" %8ld", jdprotocol ? c->joblen : c->bytelen);
  427.     putchar('\r');
  428. }
  429. #endif
  430.  
  431. /*
  432.  *    This routine is used for reading headers.
  433.  *    We don't expect to fill the buffer, so a simple scheme will do.
  434.  */
  435. void read_bytes(struct conn_info *c)
  436. {
  437.     int        count;
  438.     char        *p;
  439.  
  440.     reset_ptrs(c);
  441.     if ((count = sock_rbused(&c->sock)) > 0)
  442.     {
  443.         if (count > buffer_room(c))
  444.             count = buffer_room(c);
  445.         (void)sock_fastread(&c->sock, (byte *)c->bufip, count);
  446.         c->bufip += count;
  447.     }
  448.     if (queue_empty(c))
  449.         return;
  450. nextstate:
  451.     switch (c->state)
  452.     {
  453.     case QUEUENAME:
  454.         queuename(c);
  455.         break;
  456.     case RECVJOB:
  457.         switch (*c->buffer)
  458.         {
  459.         case '\001':
  460.             ack_cmd(c);
  461.             c->state = CLOSING;
  462.             break;
  463.         case '\002':
  464.             c->state = CONTROLINFO;
  465.             c->controlfirst = 1;
  466.             goto nextstate;
  467.         case '\003':
  468.             c->state = DATAINFO;
  469.             goto nextstate;
  470.         }
  471.     case CONTROLINFO:
  472.     case DATAINFO:
  473.         p = c->bufip;
  474.         if (buffer_room(c) == 0)
  475.             --p;
  476.         *p = '\0';
  477.         if (sscanf(c->buffer+1, "%ld", &c->bytelen) != 1)
  478.         {
  479.             (void)printf("%s: %s, error parsing %s",
  480.                 ptime(), lpt_names[c->printer], c->buffer+1);
  481.             c->state = NAKANDCLOSE;
  482.             return;
  483.         }
  484.         if (c->controlfirst && c->bytelen <= 0)    /* 0 means EOF == EOJ */
  485.             c->bytelen = 0x7fffffff;
  486.         else
  487.             ++c->bytelen;        /* including EOF byte */
  488.         ack_cmd(c);
  489.         c->jobname[0] = '\0';
  490.         c->username[0] = '\0';
  491.         c->hostname[0] = '\0';
  492.         c->state = c->state == CONTROLINFO ? CONTROL : DATA;
  493.         if (c->state == DATA)
  494.             c->starttime = time(0);
  495.         reset_ptrs(c);
  496.         break;
  497.     case CONTROL:
  498.         /* copy out interesting information from buffer */
  499.         get_jobinfo(c);
  500.         c->bytelen -= count;
  501.         if (c->bytelen <= 0)
  502.         {
  503.             (void)printf("%s: Job ", ptime());
  504.             if (c->jobname[0] != '\0')
  505.                 (void)printf("%s ", c->jobname);
  506.             if (c->username[0] != '\0' && c->hostname[0] != '\0')
  507.                 (void)printf("for %s@%s ", c->username,
  508.                     c->hostname);
  509.             (void)printf("on %s\n", lpt_names[c->printer]);
  510.             ack_cmd(c);
  511.             c->state = c->controlfirst ? DATAINFO : CLOSING;
  512.         }
  513.         break;
  514.     }
  515. }
  516.  
  517. /*
  518.  *    This routine is used for reading printable data.
  519.  *    We read as much as the available space will allow us to.
  520.  *    This may involve wraparound in buffer so the code is involved.
  521.  */
  522. void read_data(struct conn_info *c)
  523. {
  524.     int        count;
  525.     unsigned int    n, ioff, toread;
  526.     char        *p;
  527.  
  528. #ifdef    MDEBUG
  529.     if (graphmem)
  530.     {
  531.         printf("> ");
  532.         trace_queue(c);
  533.     }
  534. #endif
  535.     count = 0;
  536.     if ((n = queue_room(c)) > 0
  537.         && tcp_tick(&c->sock)
  538.         && (count = sock_rbused(&c->sock)) > 0)
  539.     {
  540.         if (count > n)
  541.             count = n;        /* we need count again later */
  542.         else
  543.             n = count;
  544.         while (n > 0)
  545.         {
  546.             ioff = c->bufip - c->buffer;    /* insertion point */
  547.             toread = buffer_size - ioff;    /* at top of buffer? */
  548.             if (toread > n)
  549.                 toread = n;
  550.             (void)sock_fastread(&c->sock, (byte *)c->bufip, toread);
  551.             ioff += toread;
  552.             n -= toread;
  553.             if (ioff >= buffer_size)    /* wrap to bottom */
  554.                 ioff = 0;
  555.             c->bufip = c->buffer + ioff;
  556.         }
  557.         c->bytelen -= count;
  558.     }
  559.     if (c->bytelen <= 0 && count > 0)    /* back over EOF byte in lpd */
  560.     {
  561.         if (c->bufip == c->buffer)
  562.             c->bufip = c->buffer + buffer_size - 1;
  563.         else
  564.             --c->bufip;
  565.     }
  566.     /* yield to printer if nothing to read or time is up */
  567.     if (count <= 0 || --c->nturns <= 0)
  568.     {
  569.         c->state = PRINTING;
  570.         n = buffer_size / 8;
  571.         c->nturns = 8 - (queue_room(c) / n);
  572.         if (c->nturns <= 0)
  573.             c->nturns = 1;
  574.     }
  575. }
  576.  
  577. /*
  578.  *    Write buffer to console for debugging
  579.  */
  580. unsigned int console_outbuf(struct conn_info *c)
  581. {
  582.     unsigned int    ioff, ooff, printed;
  583.  
  584.     ioff = c->bufip - c->buffer;
  585.     ooff = c->bufop - c->buffer;
  586.     printed = 0;
  587.     while (ooff != ioff)
  588.     {
  589.         putchar(*c->bufop++);    /* write character */
  590.         if (++ooff >= buffer_size)
  591.         {
  592.             ooff = 0;        /* wrap to bottom */
  593.             c->bufop = c->buffer;
  594.         }
  595.         ++printed;
  596.     }
  597.     return (printed);
  598. }
  599.  
  600. /*
  601.  *    Direct hardware write to printer port
  602.  */
  603. unsigned int printer_outbuf(int port, struct conn_info *c)
  604. {
  605.     int        i, j, status;
  606.     unsigned int    ioff, ooff, printed;
  607.  
  608.     ioff = c->bufip - c->buffer;
  609.     ooff = c->bufop - c->buffer;
  610.     printed = 0;
  611.     inportb(port+1);        /* read status */
  612.     status = (inportb(port+1) & 0xf8) ^ 0x48;
  613.     i = PRINT_CHUNK;
  614.     while (i-- > 0 && ooff != ioff && (status & P_READY) == P_READY)
  615.     {
  616.         outportb(port, *c->bufop++);    /* write character */
  617.         if (++ooff >= buffer_size)
  618.         {
  619.             ooff = 0;        /* wrap to bottom */
  620.             c->bufop = c->buffer;
  621.         }
  622.         outportb(port+2, 0x0d);        /* raise strobe */
  623.         outportb(port+2, 0x0c);        /* lower strobe */
  624.         ++printed;
  625.         /* sample the busy line for a few tries */
  626.         for (j = 0; j < POLL_MAX; ++j)
  627.         {
  628.             status = (inportb(port+1) & 0xf8) ^ 0x48;
  629.             if ((status & P_READY) == P_READY)
  630.                 break;
  631.         }
  632.     }
  633.     return (printed);
  634. }
  635.  
  636. void print_data(struct conn_info *c)
  637. {
  638.     int        printer, current, delta, i;
  639.     unsigned int    n, ioff, ooff;
  640.     struct lpt_info    *p;
  641.  
  642. #ifdef    MDEBUG
  643.     if (graphmem)
  644.     {
  645.         printf("< ");
  646.         trace_queue(c);
  647.     }
  648. #endif
  649.     p = &lpt[printer = c->printer];
  650.     current = bios_printer_status(printer);
  651.     /* report changes from last time */
  652.     delta = (current ^ p->status) & P_CHANGES;
  653.     if (delta)
  654.         lpt_status_change(printer, delta, current);
  655.     p->status = current;
  656.     if (toconsole)
  657.         n = console_outbuf(c);
  658.     else if (!measureonly && p->hwaddr != 0)
  659.         n = printer_outbuf(p->hwaddr, c);
  660.     else
  661.     {
  662.         ioff = c->bufip - c->buffer;
  663.         ooff = c->bufop - c->buffer;
  664.         n = 0;
  665.         /* loop, printing a PRINT_CHUNK a time to prevent hogging */
  666.         i = PRINT_CHUNK;
  667.         /* eat your heart out, Pascal */
  668.         while (i-- > 0 && ooff != ioff &&
  669.             (measureonly ||
  670.             (bios_printer_status(printer) & P_READY) == P_READY))
  671.         {
  672.             if (!measureonly)
  673.                 (void)bios_printer_outch(printer, *c->bufop);
  674.             ++c->bufop;
  675. /*
  676. *    The status returned by bios_printer_outch is useless because it
  677. *    gives the condition after printing and of course if there is no room
  678. *    or the printer is busy, it blocks
  679. */
  680.             ++n;
  681.             if (++ooff >= buffer_size)
  682.             {
  683.                 ooff = 0;
  684.                 c->bufop = c->buffer;
  685.             }
  686.         }
  687.     }
  688.     c->joblen += n;
  689.     /* yield to reader if nothing printed or time is up */
  690.     if (n <= 0 || --c->nturns <= 0)
  691.     {
  692.         c->state = DATA;
  693.         n = buffer_size / 8;
  694.         c->nturns = queue_room(c) / n;
  695.         if (c->nturns <= 0)
  696.             c->nturns = 1;
  697.     }
  698.     if (c->bytelen <= 0 && queue_empty(c))    /* all printed? */
  699.     {
  700.         /* in jdprotocol bytelen will not be 0 before EOF
  701.            well, not before maxlong bytes anyway
  702.            so reach here only in lpd protocol */
  703.         ack_cmd(c);
  704.         c->state = c->controlfirst ? CLOSING : CONTROLINFO;
  705.     }
  706. }
  707.  
  708. void show_stats(struct conn_info *c)
  709. {
  710.     time_t        elapsed;
  711.  
  712.     if (c->printer < 0)
  713.         return;
  714.     elapsed = time(0) - c->starttime;
  715.     (void)printf("%s: %s: %ld bytes %ld seconds", ptime(),
  716.         lpt_names[c->printer], c->joblen, elapsed);
  717.     if (elapsed > 0)
  718.         (void)printf(" %ld bytes/second", c->joblen / elapsed);
  719.     (void)printf("\n");
  720. }
  721.  
  722. void loop(int port, struct conn_info *c)
  723. {
  724.     unsigned int    cport;
  725.     struct sockaddr    client;
  726.     int        i;
  727.     char        *name;
  728.  
  729.     switch (c->state) {
  730.     case INIT:
  731.         if (jdprotocol)
  732.         {
  733.             if (port >= MAXLPT || lpt[port].avail != FREE)
  734.                 break;        /* don't offer connection */
  735.             cport = jdport + port;
  736.             c->printer = port;
  737.         }
  738.         else
  739.             cport = lpdport;
  740.         tcp_listen(&c->sock, cport, 0L, 0, NULL, 0);
  741.         (void)printf("%s: Connection %d listening on TCP port %u\n",
  742.             ptime(), port, cport);
  743.         c->state = WAITING;
  744.         c->nturns = MAXTURNS;
  745.         break;
  746.     case WAITING:
  747.         if (!tcp_tick(&c->sock))
  748.         {
  749.             c->state = CLOSING_NOSTATS;
  750.             break;
  751.         }
  752.         if (!sock_established(&c->sock))
  753.             break;
  754.         i = sizeof(client);
  755.         client.s_ip = 0;
  756.         name = getpeername(&c->sock, &client, &i) == 0 ?
  757.             inet_ntoa(c->buffer, client.s_ip) : "?";
  758.         if (check_access(client.s_ip))
  759.         {
  760.             (void)printf("%s: Connection %d from %s\n",
  761.                 ptime(), port, name);
  762.             c->state = jdprotocol ? DATA : QUEUENAME;
  763.             c->bytelen = 0x7fffffff;    /* maxlong */
  764.             c->starttime = time(0);
  765.             c->joblen = 0L;
  766.         }
  767.         else
  768.         {
  769.             (void)printf("%s: Connection %d from %s refused\n",
  770.                 ptime(), port, name);
  771.             c->state = CLOSING_NOSTATS;
  772.         }
  773.         reset_ptrs(c);
  774.         break;
  775.     case QUEUENAME:
  776.     case RECVJOB:
  777.     case CONTROLINFO:
  778.     case CONTROL:
  779.     case DATAINFO:
  780.         if (tcp_tick(&c->sock))
  781.             read_bytes(c);
  782.         else
  783.             c->state = CLOSING;        /* fatal */
  784.         break;
  785.     case DATA:
  786.         if (tcp_tick(&c->sock))
  787.             read_data(c);
  788.         else if (killjob)        /* sender closed connection */
  789.         {
  790.             (void)printf("%s: Job aborted by sender\n", ptime());
  791.             c->state = ABORT;
  792.         }
  793.         /* don't shutdown until all printed */
  794.         else if (!queue_empty(c))
  795.             c->state = PRINTING;
  796.         else    /* missing ack bug discovered by Steve Pacenka */
  797.         {
  798.             if (!jdprotocol)
  799.                 ack_cmd(c);
  800.             c->state = CLOSING;
  801.         }
  802.         break;
  803.     case PRINTING:
  804.         if (tcp_tick(&c->sock))
  805.             print_data(c);
  806.         else if (killjob)        /* sender closed connection */
  807.         {
  808.             (void)printf("%s: Job aborted by sender\n", ptime());
  809.             c->state = ABORT;
  810.         }
  811.         else if (!queue_empty(c))
  812.             print_data(c);
  813.         else    /* missing ack bug discovered by Steve Pacenka */
  814.         {
  815.             if (!jdprotocol)
  816.                 ack_cmd(c);
  817.             c->state = CLOSING;
  818.         }
  819.         break;
  820.     case NAKANDCLOSE:
  821.         nak_cmd(c);
  822.     case CLOSING:
  823.         show_stats(c);
  824.     case CLOSING_NOSTATS:
  825.         sock_flush(&c->sock);
  826.         sleep(1);
  827.         sock_close(&c->sock);
  828. reinit:
  829.         /* free up printer */
  830.         if (0 <= c->printer && c->printer < MAXLPT
  831.             && lpt[c->printer].avail == BUSY)
  832.             lpt[c->printer].avail = FREE;
  833.         c->state = INIT;
  834.         c->controlfirst = 0;
  835.         c->printer = -1;
  836.         break;
  837.     case ABORT:
  838.         sock_abort(&c->sock);
  839.         goto reinit;
  840.     }
  841. }
  842.  
  843. void check_key(void)
  844. {
  845.     int        key, i;
  846.  
  847.     if (((key = bioskey(0)) & 0xff) != 0)
  848.         return;            /* ASCII key */
  849.     key = (key >> 8) & 0xff;
  850.     if (key < CF1 || key > CF3)
  851.         return;
  852.     key -= CF1;        /* which printer? */
  853.     for (i = 0; i < MAXCON; ++i)
  854.     {
  855.         if (conn[i].printer == key && conn[i].state != INIT)
  856.         {
  857.             if (reinit)
  858.             {
  859.                 (void)printf("%s: Aborting job and reinitialising %s\n",
  860.                     ptime(), lpt_names[key]);
  861.                 (void)biosprint(1, 0, key);
  862.                 sleep(2);        /* let printer settle */
  863.             }
  864.             else
  865.             {
  866.                 (void)printf("%s: Aborting job on %s\n",
  867.                     ptime(), lpt_names[key]);
  868.             }
  869.             conn[i].state = ABORT;        /* abort job */
  870.             return;
  871.         }
  872.     }
  873. }
  874.  
  875. void make_list(char *adjective, char *s,
  876.     longword table[], int *nentries, int maxtable)
  877. {
  878.     char        *p;
  879.     longword    ip;
  880.     int        i;
  881.     char        buffer[64];
  882.  
  883.     for ( ; *s != '\0'; s = p + 1)
  884.     {
  885.         if ((p = strchr(s, ',')) == 0)
  886.             p = s + strlen(s) - 1;
  887.         else
  888.             *p = '\0';        /* mark end */
  889.         if ((ip = resolve(s)) == (longword)0)
  890.             continue;
  891.         if (*nentries >= maxtable)
  892.             continue;        /* should print warning */
  893.         table[(*nentries)++] = ip;
  894.     }
  895.     for (i = 0; i < *nentries; ++i)
  896.         (void)printf("%s ", inet_ntoa(buffer, table[i]));
  897.     if (*nentries > 0)
  898.         (void)printf("%s access\n", adjective);
  899. }
  900.  
  901. void my_init(char *name, char *value)
  902. {
  903.     if (strcmp(name, "PRINTER1NAME") == 0)
  904.     {
  905.         strncpy(lpt_names[0], value, sizeof(lpt_names[0]));
  906.         lpt_names[0][sizeof(lpt_names[0])-1] = '\0';
  907.     }
  908.     else if (strcmp(name, "PRINTER2NAME") == 0)
  909.     {
  910.         strncpy(lpt_names[1], value, sizeof(lpt_names[1]));
  911.         lpt_names[1][sizeof(lpt_names[1])-1] = '\0';
  912.     }
  913.     else if (strcmp(name, "PRINTER3NAME") == 0)
  914.     {
  915.         strncpy(lpt_names[2], value, sizeof(lpt_names[2]));
  916.         lpt_names[2][sizeof(lpt_names[2])-1] = '\0';
  917.     }
  918.     else if (normal_init)
  919.         (*normal_init)(name, value);
  920. }
  921.  
  922. void options(int argc, char **argv)
  923. {
  924.     int        i;
  925.     char        *s;
  926.     extern int    optind;
  927.     extern char    *optarg;
  928.     extern int    getopt(int, char **, char *);
  929.  
  930.     for (i = 0; i < MAXLPT; ++i)
  931.         lpt[i].hwaddr = 0;
  932.     while ((i = getopt(argc, argv, "123a:b:cd:gij:kl:mn:p:st")) > 0)
  933.     {
  934.         switch (i)
  935.         {
  936.         case '1': case '2': case '3':
  937.             lpt[i - '1'].avail  = DISABLED;
  938.             break;
  939.         case 'a':
  940.             if (ndeny <= 0)
  941.                 make_list("allowed", optarg, allow, &nallow, MAXALLOW);
  942.             break;
  943.         case 'b':
  944.             for (s = optarg; '1' <= *s && *s <= '3'; ++s)
  945.                 lpt[*s - '1'].hwaddr = 1;
  946.             break;
  947.         case 'c':
  948.             toconsole = 1;
  949.             break;
  950.         case 'd':
  951.             if (nallow <= 0)
  952.                 make_list("denied", optarg, deny, &ndeny, MAXDENY);
  953.             break;
  954.         case 'g':
  955.             graphmem = 1;
  956.             break;
  957.         case 'i':
  958.             reinit = 1;
  959.             break;
  960.         case 'j':
  961.             jdprotocol = 1;
  962.             if ((jdport = atoi(optarg)) <= 0)
  963.                 jdport = JDPORT;
  964.             break;
  965.         case 'k':
  966.             killjob = 0;
  967.             break;
  968.         case 'l':
  969.             loghost = optarg;
  970.             break;
  971.         case 'm':
  972.             measureonly = 1;
  973.             break;
  974.         case 'n':
  975.             if ((nlpt = atoi(optarg)) > MAXLPT || nlpt < 0)
  976.                 nlpt = 0;
  977.             break;
  978.         case 'p':        /* alternate TCP port */
  979.             if ((lpdport = atoi(optarg)) <= 0)
  980.                 lpdport = LPDPORT;
  981.             break;
  982.         case 's':
  983.             check_subnet = 0;
  984.             break;
  985.         case 't':
  986.             notone = 1;
  987.             break;
  988.         }
  989.     }
  990. }
  991.  
  992. int main(int argc, char **argv)
  993. {
  994.     int        i;
  995.  
  996.     (void)printf(PROGRAM " " VERSION " " AUTHOR "\n");
  997.     (void)printf(PROGRAM " comes with ABSOLUTELY NO WARRANTY.\n");
  998.     (void)printf("This is Postcardware, and you are welcome to redistribute it\n");
  999.     (void)printf(" under certain conditions; see the file Artistic for details.\n");
  1000.     (void)printf("C-F1 through C-F3 to abort printer jobs\n");
  1001.     (void)memset(conn, 0, sizeof(conn));
  1002.     (void)memset(lpt, 0, sizeof(lpt));
  1003.     tzset();
  1004.     options(argc, argv);
  1005.     /* hook onto init procedure to get NAME=VALUE pairs */
  1006.     normal_init = usr_init;
  1007.     usr_init = my_init;
  1008.     dbuginit();
  1009.     sock_init();
  1010.     init_queues();
  1011.     if (nlpt == 0)
  1012.     {
  1013.         (void)printf("%s: No printers connected\n", ptime());
  1014.         return (1);
  1015.     }
  1016.     init_log();
  1017.     for (;;)
  1018.     {
  1019.         for (i = 0; i < MAXCON; ++i)
  1020.             loop(i, &conn[i]);
  1021.         if (kbhit())
  1022.             check_key();
  1023.     }
  1024.     /*NOTREACHED*/
  1025.     return (0);
  1026. }
  1027.